﻿using System;
using SharpCompress.Compressors.Rar.VM;

namespace SharpCompress.Compressors.Rar.UnpackV1;

internal static class UnpackUtility
{
    //!!! TODO rename methods
    internal static uint DecodeNumber(this BitInput input, Decode.Decode dec) =>
        (uint)input.decodeNumber(dec);

    internal static int decodeNumber(this BitInput input, Decode.Decode dec)
    {
        int bits;
        long bitField = input.GetBits() & 0xfffe;

        //        if (bitField < dec.getDecodeLen()[8]) {
        //			if (bitField < dec.getDecodeLen()[4]) {
        //				if (bitField < dec.getDecodeLen()[2]) {
        //					if (bitField < dec.getDecodeLen()[1]) {
        //						bits = 1;
        //					} else {
        //						bits = 2;
        //					}
        //				} else {
        //					if (bitField < dec.getDecodeLen()[3]) {
        //						bits = 3;
        //					} else {
        //						bits = 4;
        //					}
        //				}
        //			} else {
        //				if (bitField < dec.getDecodeLen()[6]) {
        //					if (bitField < dec.getDecodeLen()[5])
        //						bits = 5;
        //					else
        //						bits = 6;
        //				} else {
        //					if (bitField < dec.getDecodeLen()[7]) {
        //						bits = 7;
        //					} else {
        //						bits = 8;
        //					}
        //				}
        //			}
        //		} else {
        //			if (bitField < dec.getDecodeLen()[12]) {
        //				if (bitField < dec.getDecodeLen()[10])
        //					if (bitField < dec.getDecodeLen()[9])
        //						bits = 9;
        //					else
        //						bits = 10;
        //				else if (bitField < dec.getDecodeLen()[11])
        //					bits = 11;
        //				else
        //					bits = 12;
        //			} else {
        //				if (bitField < dec.getDecodeLen()[14]) {
        //					if (bitField < dec.getDecodeLen()[13]) {
        //						bits = 13;
        //					} else {
        //						bits = 14;
        //					}
        //				} else {
        //					bits = 15;
        //				}
        //			}
        //		}
        //		addbits(bits);
        //		int N = dec.getDecodePos()[bits]
        //				+ (((int) bitField - dec.getDecodeLen()[bits - 1]) >>> (16 - bits));
        //		if (N >= dec.getMaxNum()) {
        //			N = 0;
        //		}
        //		return (dec.getDecodeNum()[N]);
        var decodeLen = dec.DecodeLen;
        if (bitField < decodeLen[8])
        {
            if (bitField < decodeLen[4])
            {
                if (bitField < decodeLen[2])
                {
                    if (bitField < decodeLen[1])
                    {
                        bits = 1;
                    }
                    else
                    {
                        bits = 2;
                    }
                }
                else
                {
                    if (bitField < decodeLen[3])
                    {
                        bits = 3;
                    }
                    else
                    {
                        bits = 4;
                    }
                }
            }
            else
            {
                if (bitField < decodeLen[6])
                {
                    if (bitField < decodeLen[5])
                    {
                        bits = 5;
                    }
                    else
                    {
                        bits = 6;
                    }
                }
                else
                {
                    if (bitField < decodeLen[7])
                    {
                        bits = 7;
                    }
                    else
                    {
                        bits = 8;
                    }
                }
            }
        }
        else
        {
            if (bitField < decodeLen[12])
            {
                if (bitField < decodeLen[10])
                {
                    if (bitField < decodeLen[9])
                    {
                        bits = 9;
                    }
                    else
                    {
                        bits = 10;
                    }
                }
                else if (bitField < decodeLen[11])
                {
                    bits = 11;
                }
                else
                {
                    bits = 12;
                }
            }
            else
            {
                if (bitField < decodeLen[14])
                {
                    if (bitField < decodeLen[13])
                    {
                        bits = 13;
                    }
                    else
                    {
                        bits = 14;
                    }
                }
                else
                {
                    bits = 15;
                }
            }
        }
        input.AddBits(bits);
        var N =
            dec.DecodePos[bits]
            + (Utility.URShift(((int)bitField - decodeLen[bits - 1]), (16 - bits)));
        if (N >= dec.MaxNum)
        {
            N = 0;
        }
        return (dec.DecodeNum[N]);
    }

    internal static void makeDecodeTables(
        Span<byte> lenTab,
        int offset,
        Decode.Decode dec,
        int size
    )
    {
        Span<int> lenCount = stackalloc int[16];
        Span<int> tmpPos = stackalloc int[16];
        int i;
        long M,
            N;

        new Span<int>(dec.DecodeNum).Clear(); // memset(Dec->DecodeNum,0,Size*sizeof(*Dec->DecodeNum));

        for (i = 0; i < size; i++)
        {
            lenCount[lenTab[offset + i] & 0xF]++;
        }
        lenCount[0] = 0;
        for (tmpPos[0] = 0, dec.DecodePos[0] = 0, dec.DecodeLen[0] = 0, N = 0, i = 1; i < 16; i++)
        {
            N = 2 * (N + lenCount[i]);
            M = N << (15 - i);
            if (M > 0xFFFF)
            {
                M = 0xFFFF;
            }
            dec.DecodeLen[i] = (int)M;
            tmpPos[i] = dec.DecodePos[i] = dec.DecodePos[i - 1] + lenCount[i - 1];
        }

        for (i = 0; i < size; i++)
        {
            if (lenTab[offset + i] != 0)
            {
                dec.DecodeNum[tmpPos[lenTab[offset + i] & 0xF]++] = i;
            }
        }
        dec.MaxNum = size;
    }
}
